package gis

import (
	"fmt"
	"gitee.com/ymofen/gobase"
	"math"
)

// 使用格网点内插计算值, 计算内插值列表
// 格网点的bl并不一定和格网点完全一致, 使用实际的bl进行计算权重
// create:2024-05-10 16:35:28
type GisInterposeVlst struct {
	cnt          int
	datatag      int32
	invalidValue float64

	gridOpt *TGisGridV3
	xFieldN int
	yFieldN int

	bllst []BLPoint
	lst   [][]float64
}

func NewGisInterposVlst() *GisInterposeVlst {
	rval := &GisInterposeVlst{gridOpt: &TGisGridV3{}}
	return rval
}

func (this *GisInterposeVlst) InvalidVal() float64 {
	return this.invalidValue
}

func (this *GisInterposeVlst) ConfigOptions(startlng, startlat, endlng, endlat, gridlng, gridlat float64) {
	x0 := float64(math.Pow10(6))
	gridlng = math.Round(gridlng*x0) / x0
	gridlat = math.Round(gridlat*x0) / x0
	xN := (endlng - startlng) / gridlng
	this.xFieldN = int(math.Ceil(xN)) + 1                        // 数量:+1
	this.yFieldN = int(math.Ceil((endlat-startlat)/gridlat)) + 1 // 数量:+1

	this.gridOpt.GridLng = gridlng
	this.gridOpt.GridLat = gridlat
	this.gridOpt.StartLat = startlat
	this.gridOpt.StartLng = startlng
	this.lst = make([][]float64, this.xFieldN*this.yFieldN)
	this.bllst = make([]BLPoint, this.xFieldN*this.yFieldN)
}

func (this *GisInterposeVlst) Count() int {
	return this.cnt
}

func (this *GisInterposeVlst) Status() string {
	opt := this.gridOpt
	lst := this.lst
	if lst == nil || opt == nil {
		return "NULL"
	}
	return fmt.Sprintf("cnt=%d;startlng=%.4f;startlat:%.4f;gridlng=%.4f;gridlat=%.4f", len(lst), opt.StartLng, opt.StartLat, opt.GridLng, opt.GridLat)
}

func (this *GisInterposeVlst) AddVallst(b, l float64, vallst []float64) error {
	opt := this.gridOpt
	x := int(math.Round((l - opt.StartLng) / opt.GridLng))
	y := int(math.Round((b - opt.StartLat) / opt.GridLat))
	if x < 0 || y < 0 {
		return fmt.Errorf("非法的数据记录")
	}

	idx := (y * this.xFieldN) + x
	this.lst[idx] = vallst
	this.bllst[idx].B = b
	this.bllst[idx].L = l
	return nil
}

// 矩阵定位
// 返回实际的b0, l0和值列表
func (this *GisInterposeVlst) FindBaseItemByBLEx(b, l float64) (b0, l0 float64, vlst []float64) {

	opt := this.gridOpt
	x := int(math.Round((l - opt.StartLng) / opt.GridLng))
	y := int(math.Round((b - opt.StartLat) / opt.GridLat))
	idx := (y * this.xFieldN) + x
	if idx >= len(this.lst) {
		return
	}
	if idx < 0 {
		return
	}
	return this.bllst[idx].B, this.bllst[idx].L, this.lst[idx]
}

func (this *GisInterposeVlst) FindNearest4Grid(b, l float64) (rval [4]BLPoint) {
	return this.gridOpt.FindNearest4Grid(b, l)
}

func (this *GisInterposeVlst) GetValue(b, l float64, sb *gobase.BytesBuilder) (ok bool, v []float64) {
	lst := this.lst
	gridOpt := this.gridOpt
	if lst == nil {
		return
	}
	plst := gridOpt.FindNearest4Grid(b, l)
	return this.InnerGetValue(plst, l, b, sb)
}

// 返回值列表: 内插列表值
func (this *GisInterposeVlst) InnerGetValue(gridlst [4]BLPoint, l, b float64, sb *gobase.BytesBuilder) (ok bool, vlst []float64) {

	t0, t1, f0, f1 := gridlst[0], gridlst[1], gridlst[2], gridlst[3]

	type calcitm struct {
		b, l float64
		lst  []float64
	}

	lst1 := make([]*calcitm, 0, 4)

	var checkAddPtr = func(t0 BLPoint) {
		b0, l0, lst0 := this.FindBaseItemByBLEx(t0.B, t0.L)
		if len(lst0) > 0 {
			itm := &calcitm{}
			itm.b = b0
			itm.l = l0
			itm.lst = lst0
			lst1 = append(lst1, itm)
		}
	}

	checkAddPtr(t0)
	checkAddPtr(t1)
	checkAddPtr(f0)
	checkAddPtr(f1)

	if len(lst1) > 0 {

		vlst = make([]float64, len(lst1[0].lst))

		// 距离集合
		dlst := make([]float64, len(lst1))
		var sum_1_di float64
		for i := 0; i < len(lst1); i++ {
			b0 := lst1[i].b
			l0 := lst1[i].l

			dlst[i] = CalcuDis(l, b, l0, b0) // 必须为正数
			if dlst[i] == 0 {
				dlst[i] = 10 ^ -48 // 如果为0, 其实可以直接取偏差,
			}
			sum_1_di += 1 / dlst[i]
		}

		// 权重, 距离越近，权重越高, 如果不够平滑 可以加上平方
		wlst := make([]float64, len(lst1))
		for i := 0; i < len(dlst); i++ {
			wlst[i] = (1 / dlst[i]) / sum_1_di
		}

		for i := 0; i < len(lst1); i++ {
			b0 := lst1[i].b
			l0 := lst1[i].l
			v0 := lst1[i].lst
			w0 := wlst[i]

			if sb != nil {
				sb.Appendf("格网点:[%.4f-%.4f],值:%.20f, 权重:%.20f,距离:%.2f\r\n", l0, b0, v0, w0, dlst[i])
			}
			for j := 0; j < len(v0); j++ {
				vlst[j] += w0 * v0[j]
				if sb != nil {
					sb.Appendf("  [%d]值:%.20f, 计算值:%.20f\r\n", j, v0[j], vlst[j])
				}
			}
		}

		ok = true
	}
	return
}
